package com.huatai.model.service;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ZipUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huatai.common.config.MinioAPI;
import com.huatai.common.constant.KafkaConstant;
import com.huatai.common.error.exception.ServiceException;
import com.huatai.common.util.DateTimeUtil;
import com.huatai.common.util.NumberUtil;
import com.huatai.common.util.SpringUtil;
import com.huatai.common.util.StringUtil;
import com.huatai.model.config.CustomMinioClient;
import com.huatai.model.constant.MinioBucket;
import com.huatai.model.entity.ModelEntity;
import com.huatai.model.mapper.DictionaryMapper;
import com.huatai.model.mapper.ModelMapper;
import io.minio.GetObjectArgs;
import io.minio.GetObjectResponse;
import io.minio.ObjectWriteResponse;
import io.minio.PutObjectArgs;
import io.minio.errors.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.DosFileAttributes;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * @author Lion
 * @date 2024/4/7  11:09
 */
@Service
@Slf4j
public class ModelService extends ServiceImpl<ModelMapper, ModelEntity> {

	@Value("${minio.PREURL}")
	private String minioEndpoint ;

	@Autowired
	private FileService fileService;

	@Autowired
	private MinioAPI minioAPI;

	@Autowired
	DictionaryMapper dictionaryMapper;

	@Autowired
	CustomMinioClient customMinioClient;

	@Lazy
	@Autowired
	ModelService modelService;

	@Autowired
	private KafkaTemplate<String, Object> kafkaTemplate;

	/**
	 * 用于自身异步执行更新
	 */
//	@Autowired
//	ModelService modelService;

	private final static String SLASH = "/";

	public IPage<ModelEntity> pageByRequestMap(Map params) {
		int index = Integer.valueOf(params.getOrDefault("pageIndex", 1).toString());
		;
		int size = Integer.valueOf(params.getOrDefault("pageSize", 10).toString());

		index = index < 1 ? 1 : index;
		size = size < 1 ? 10 : size;

		LambdaQueryWrapper<ModelEntity> qw = Wrappers.lambdaQuery();
		IPage<ModelEntity> page = new Page<ModelEntity>(index, size);
		qw.like(StringUtil.isNotBlank((String) params.get("modelName")), ModelEntity::getModelName, (String) params.get("modelName"));
		qw.eq(StringUtil.isNotBlank((String) params.get("typeKey")), ModelEntity::getTypeKey, (String) params.get("typeKey"));
		qw.eq(StringUtil.isNotBlank((String) params.get("useKey")), ModelEntity::getUseKey, (String) params.get("useKey"));
		qw.eq(StringUtil.isNotBlank((String) params.get("industryKey")), ModelEntity::getIndustryKey, (String) params.get("industryKey"));

		String label = (String) params.get("label");
		if (StringUtil.isNotBlank(label)) {
			qw.and(wrapper -> {
				wrapper.or().eq(ModelEntity::getTypeKey, label);
				wrapper.or().eq(ModelEntity::getUseKey, label);
				wrapper.or().eq(ModelEntity::getIndustryKey, label);
			});
		}

		qw.orderBy(true, false, ModelEntity::getCreateTime);

		return this.page(page, qw);
	}

	public List<ModelEntity> list(JSONObject params) {
		LambdaQueryWrapper<ModelEntity> qw = Wrappers.lambdaQuery();
		qw.like(StringUtil.isNotBlank((String) params.get("modelName")), ModelEntity::getModelName, (String) params.get("modelName"));
		qw.eq(StringUtil.isNotBlank((String) params.get("typeKey")), ModelEntity::getTypeKey, (String) params.get("typeKey"));
		qw.eq(StringUtil.isNotBlank((String) params.get("useKey")), ModelEntity::getUseKey, (String) params.get("useKey"));
		qw.eq(StringUtil.isNotBlank((String) params.get("industryKey")), ModelEntity::getIndustryKey, (String) params.get("industryKey"));

		String label = (String) params.get("label");
		if (StringUtil.isNotBlank(label)) {
			qw.and(wrapper -> {
				wrapper.or().eq(ModelEntity::getTypeKey, label);
				wrapper.or().eq(ModelEntity::getUseKey, label);
				wrapper.or().eq(ModelEntity::getIndustryKey, label);
			});
		}
		qw.orderBy(true, false, ModelEntity::getCreateTime);
		return super.list(qw);
	}

	@Transactional(rollbackFor = Exception.class)
	public boolean SaveOrUpdateWithValid(ModelEntity data) {

		//处理模型文件类型,已文件后缀作为文件类型
		if (StringUtil.isNotBlank(data.getFbxFile())) {
			String fileType = data.getFbxFile().substring(data.getFbxFile().lastIndexOf(".") + 1);
			data.setFbxFileType(fileType);
			data.setFbxFileSize(fileService.getFileSize(data.getFbxFile()));
		}

		LambdaQueryWrapper<ModelEntity> wrapper = Wrappers.lambdaQuery();
		wrapper.eq(ModelEntity::getModelName, data.getModelName());
		ModelEntity oldModel = super.getOne(wrapper);
		if (ObjectUtil.isNotNull(oldModel) && !oldModel.getId().equals(data.getId())) {
			throw new ServiceException("模型已存在，请勿重复添加");
		}

		//保存数据
		if (null == data.getId() || data.getId().longValue() == 0) {
			//baseMapper.insert(data);
			//新增处理文件逻辑
			save(data);
			modelService.uploadModelZipFbx(data);
			//save(data);
		} else {
			updateById(data);
			//根据模型文件判断是否需要执行文件处理
			ModelEntity model = getById(data.getId());
			if (!model.getFbxFile().equals(data.getFbxFile()) || !model.getZipFile().equals(data.getZipFile())) {
				modelService.uploadModelZipFbx(data);
			}
			//saas服务监听修改内容
			JSONObject mdendit = new JSONObject();
			//List<Long> idlist = Arrays.asList(data.getId());
			mdendit.put("md_id",data.getId());
			//endit_type类型1为修改2删除
			mdendit.put("edit_type",1);
//			kafkaTemplate.send(KafkaConstant.MODEL_EDIT_INFO,mdendit.toJSONString());
			TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
				@Override
				public void afterCommit() {
					kafkaTemplate.send(KafkaConstant.MODEL_EDIT_INFO, mdendit.toJSONString());
				}
			});

		}

		return true;
	}

	/**
	 * 异步执行更新压缩文件或fbx
	 *
	 * @param data
	 */
	@Async("threadPoolExecutor")
	public void uploadModelZipFbx(ModelEntity data) {
		String path = DateTimeUtil.format(LocalDate.now(), "yyyyMMdd") + "/" + System.currentTimeMillis() + "/";
		if (data.getModelType().intValue() == 1) {
			//执行压缩zip
			List<String> files = new ArrayList();
			//获取fbx文件名
			String fbxname = data.getFbxFile().substring(data.getFbxFile().lastIndexOf("/"));
			//写入本地文件
			if (StringUtil.isEmpty(data.getFbxFile())) {
				return;
			}
			files.add(data.getFbxFile());
			if (StringUtil.isNotBlank(data.getDiffuseMap())) {
				files.add(data.getDiffuseMap());
			}

			if (StringUtil.isNotBlank(data.getMirrorMap())) {
				files.add(data.getMirrorMap());
			}

			if (StringUtil.isNotBlank(data.getNormalMap())) {
				files.add(data.getNormalMap());
			}

			//保存压缩文件名称
			String zipName = fbxname.replace(".fbx", "").replace("/", "") + ".zip";

			try {
				zipFilesFromMinIO(files, zipName);
			} catch (Exception e) {
				log.error("文件压缩失败了");
			}
			String pp = System.getProperty("user.dir") + "/" + zipName;
			File file = new File(pp);
			FileInputStream inputStream = null;
			try {
				inputStream = new FileInputStream(file);
				//zipFilesFromMinIO(files,"test.zip");
			} catch (FileNotFoundException e) {
				log.error("获取文件失败");
			}
			//minioAPI.uploadInputStream(MinioBucket.MODEL_BUCKET_NAME_PRIVATE,file.getName(),inputStream,"zip");
			try {
				minioAPI.getMinioClient().putObject(PutObjectArgs.builder()
					.bucket(MinioBucket.MODEL_BUCKET_NAME_PRIVATE)
					.object(path + file.getName())
					.stream(inputStream, -1L, 10485760L)
					.build()
				);
			} catch (Exception e) {
				log.error("压缩文件上传失败");
			}
			try {
				inputStream.close();
			} catch (IOException e) {
				log.error("文件流关闭失败");
			}

			if (!file.delete()) {
				log.error("删除临时压缩文件失败");
			}
			//回填压缩文件路径
			data.setZipFile(path + file.getName());
			updateById(data);

		} else if (data.getModelType().intValue() == 2) {
			//执行解压获取fbx文件
			ZipInputStream zis = null;
			try {
				InputStream is = minioAPI.getMinioClient().getObject(GetObjectArgs.builder().bucket(MinioBucket.MODEL_BUCKET_NAME_PRIVATE).object(data.getZipFile()).build());

				String pp = System.getProperty("user.dir") + "/" + System.currentTimeMillis();
				File file = new File(pp);
				zis = new ZipInputStream(is, Charset.forName("GBK"));
				File newfile = ZipUtil.unzip(zis, file);
				File[] files = newfile.listFiles();
				for (File fname : files) {
					if (fname.isFile()) {
						String fnameName = fname.getName();
						String pathFile = path + fnameName;
						// 获取后缀
						String suffix = fnameName.substring(fnameName.lastIndexOf("."));
						InputStream inputStream = new FileInputStream(fname);
						minioAPI.getMinioClient().putObject(PutObjectArgs.builder()
							.bucket(MinioBucket.MODEL_BUCKET_NAME_PUBLIC)
							.object(pathFile)
							.stream(inputStream, inputStream.available(), -1)
							.build()
						);
						inputStream.close();
						//					//回写fbx文件路径到模型库
						if (suffix.equals(".fbx")) {
							   update(null, Wrappers.<ModelEntity>lambdaUpdate()
								.set(ModelEntity::getFbxFile, pathFile).set(ModelEntity::getFbxFileType,"fbx").eq(ModelEntity::getId, data.getId()));

						}
					}
				}

				delete(newfile);
//				if (file.delete()) {
//					System.out.println("回删成");
//				}else {
//					System.out.println("回删失败");
//				}

				// 创建ZipInputStream，用于读取ZIP文件
//				zis = new ZipInputStream(is, Charset.forName("GBK"));
//				// 遍历ZIP文件中的所有条目
//				ZipEntry entry;
//				ByteArrayOutputStream bos = new ByteArrayOutputStream();
//				byte[] bytes = new byte[1024];
//				while ((entry = zis.getNextEntry()) != null) {
//					String filename = entry.getName();
//
////					String fileType = getFileType(filename);
////					System.out.println("========"+fileType);
//
//					String pathFile = path + filename;
//					// 获取后缀
//					String suffix = filename.substring(filename.lastIndexOf("."));
//
//
//					// 输出文件
//					if (entry.isDirectory()) {
//						new File(entry.getName()).mkdirs();
//					} else {
//						while (true) {
//							int len = zis.read(bytes);
//							if (len <= 0) {
//								break;
//							}
//							bos.write(bytes);
//						}
//					}
//					bos.flush();
//					bos.close();
//					zis.closeEntry();
//					InputStream bis = new ByteArrayInputStream(bos.toByteArray());
//					//上传单文件到minio
//
//					//minioAPI.uploadInputStream(MinioBucket.MODEL_BUCKET_NAME_PUBLIC,pathFile,bis,fileType);
//					minioAPI.getMinioClient().putObject(PutObjectArgs.builder()
//						.bucket(MinioBucket.MODEL_BUCKET_NAME_PUBLIC)
//						.object(pathFile)
//						.stream(bis, bis.available(), -1)
//						.build()
//					);
//					//回写fbx文件路径到模型库
//					if (suffix.equals(".fbx")) {
//						data.setFbxFile(pathFile);
//						updateById(data);
//					}
//
//					bis.close();
//				}

			} catch (Exception e) {
				log.error("文件不存在");
			}


		}

	}

	public static void delete(File folder) {
		if(folder.isDirectory()) {
			File[] files = folder.listFiles();
			if(files != null) { // 如果文件夹为空，files可能为null
				for(File file : files) {
					delete(file); // 递归删除子文件夹和文件
				}
			}
		}
		folder.delete(); // 删除空文件夹或者文件
	}

	/**
	 * 压缩文件列表
	 *
	 * @param fileNames
	 * @param zipFileName
	 * @throws Exception
	 */
	public void zipFilesFromMinIO(List<String> fileNames, String zipFileName) throws Exception {
		try (ZipArchiveOutputStream zipOut = new ZipArchiveOutputStream(new FileOutputStream(zipFileName))) {
			for (String fileName : fileNames) {
				String fname = fileName.substring(fileName.lastIndexOf("/")).replace("/", "");
				// 从MinIO获取文件输入流
				InputStream inputStream = minioAPI.getMinioClient().getObject(GetObjectArgs.builder().bucket(MinioBucket.MODEL_BUCKET_NAME_PUBLIC).object(fileName).build());
				// 设置压缩条目
				//ZipArchiveEntry zipEntry = new ZipArchiveEntry(fileName);
				ZipArchiveEntry zipEntry = new ZipArchiveEntry(fname);
				zipOut.putArchiveEntry(zipEntry);
				// 复制数据到压缩文件
				IOUtils.copy(inputStream, zipOut);
				zipOut.closeArchiveEntry();
				IOUtils.closeQuietly(inputStream);
			}
		}
	}


	/**
	 * 处理文件为绝对路径
	 */
	public void buildExInfo(ModelEntity entity) {
		if (null == entity) {
			return;
		}
		entity.setFbxFileyl(getFileAbsolutePath(entity.getFbxFile()));
		entity.setZipFileXz(getFileAbsolutePath(entity.getZipFile()));
		entity.setDiffuseMapyl(getFileAbsolutePath(entity.getDiffuseMap()));
		entity.setMirrorMapyl(getFileAbsolutePath(entity.getMirrorMap()));
		entity.setNormalMapyl(getFileAbsolutePath(entity.getNormalMap()));
		entity.setMasterMapyl(getFileAbsolutePath(entity.getMasterMap()));
		entity.setSimpleMapyl(getFileAbsolutePath(entity.getSimpleMap()));
		entity.setIndustryKeyName(dictionaryMapper.getNameByKey(entity.getIndustryKey()));

	}

	/**
	 * 处理文件为绝对路径
	 */
	public void buildExInfo(List<ModelEntity> list) {
		if (null != list && list.size() > 0) {
			list.stream().forEach(item -> {
				buildExInfo(item);
				item.setFbxFileyl(null);
			});
		}
	}

	/**
	 * 获取文件绝对路径
	 *
	 * @param fileName
	 * @return
	 */
	public String getFileAbsolutePath(String fileName) {
		if (StringUtil.isBlank(fileName)) {
			return null;
		}
		String bucketName = fileService.getBucketName(fileName);
		String path = null;
		if (MinioBucket.MODEL_BUCKET_NAME_PRIVATE.equals(bucketName)) {
			try {
				path = minioAPI.getSignedUrl(bucketName, fileName, 86400);
			} catch (Exception e) {
			}
		} else {
			path = minioEndpoint + SLASH + MinioBucket.MODEL_BUCKET_NAME_PUBLIC + SLASH + fileName;
		}
		return path;
	}
}
